package com.j.lemon.learn.basic.OkHttp3;

import com.alibaba.fastjson.JSONObject;
import okhttp3.Authenticator;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.*;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;


public class OkHttp3Util {

    //代理服务器
    private static final String proxyAddr = "172.21.16.13";
    private static final int proxyPort = 3128;
    private static final String proxyUser = "http_proxy";
    private static final String proxyPwd = "eMqaG4cfuo";


    private static final Logger logger = LoggerFactory.getLogger(OkHttp3Util.class);

    private OkHttp3Util() {
    }


    /**
     * 获取一个访问https的client，信任所有证书
     *
     * @return OkHttpClient
     */
    public static OkHttpClient getHttpsClientTrustAll() {
        OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                .hostnameVerifier(new TrustAllHostnameVerifier())
                .build();
        return okHttpClient;
    }

    private static class TrustAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            logger.error("构造SSL访问失败！", e);
        }
        return ssfFactory;
    }

    private static class TrustAllCerts implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params) throws IOException {
        return doPost(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @return
     */
    public static Response doPost(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doPost(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPost(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     */
    public static Response doPost(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPost(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }


    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @return
     */
    public static Response doPost(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();


        FormBody.Builder builder = new FormBody.Builder();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry entry : params.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                builder.add(key, value);
            }
        }
        RequestBody body = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        return retryProcess(okHttpClient, request, retry);
    }


    /**
     *
     * @param url
     * @param jsonStr
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr) throws IOException {
        return doPostJSON(url, jsonStr, 0, 0, false);
    }

    /**
     *
     * @param url
     * @param jsonStr
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, boolean useProxy) throws IOException {
        return doPostJSON(url, jsonStr, 0, 0, useProxy);
    }

    /**
     *
     * @param url
     * @param jsonStr
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int timeOut, int retry) throws IOException {
        return doPostJSON(url, jsonStr, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     *
     * @param url
     * @param jsonStr
     * @param timeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPostJSON(url, jsonStr, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     *
     * @param url
     * @param jsonStr
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @param useProxy
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, String jsonStr, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");

        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();
        JSONObject jsonObj = JSONObject.parseObject(jsonStr);

        RequestBody body = RequestBody.create(JSON, jsonObj.toJSONString());

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        return retryProcess(okHttpClient, request, retry);
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params) throws IOException {
        return doPostJSON(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @return
     */
    public static Response doPostJSON(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doPostJSON(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doPostJSON(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     */
    public static Response doPostJSON(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doPostJSON(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @return
     */
    public static Response doPostJSON(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");

        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();
        JSONObject jsonObj = JSONObject.parseObject(JSONObject.toJSONString(params));

        RequestBody body = RequestBody.create(JSON, jsonObj.toJSONString());

        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();

        return retryProcess(okHttpClient, request, retry);
    }

    /**
     * @param request
     * @param isHttps
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, boolean isHttps) throws IOException {
        return doPostJSON(request, 0, 0, isHttps, false);
    }

    /**
     * @param request
     * @return
     */
    public static Response doPostJSON(Request request, boolean isHttps, boolean useProxy) throws IOException {
        return doPostJSON(request, 0, 0, isHttps, useProxy);
    }

    /**
     * @param request
     * @param timeOut
     * @param retry
     * @param isHttps
     * @return
     * @throws IOException
     */
    public static Response doPostJSON(Request request, int timeOut, int retry, boolean isHttps) throws IOException {
        return doPostJSON(request, timeOut, timeOut, timeOut, retry, isHttps, false);
    }

    /**
     * @param request
     * @param timeOut
     * @param retry
     * @return
     */
    public static Response doPostJSON(Request request, int timeOut, int retry, boolean isHttps, boolean useProxy) throws IOException {
        return doPostJSON(request, timeOut, timeOut, timeOut, retry, isHttps, useProxy);
    }


    /**
     * @param request
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @return
     */
    public static Response doPostJSON(Request request, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean isHttps, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, null);
        if (isHttps) {
            okHttpClientBuilder.sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                    .hostnameVerifier(new TrustAllHostnameVerifier());
        }
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        return retryProcess(okHttpClientBuilder.build(), request, retry);
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params) throws IOException {
        return doGet(url, params, 0, 0, false);
    }

    /**
     * @param url
     * @param params
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, boolean useProxy) throws IOException {
        return doGet(url, params, 0, 0, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int timeOut, int retry) throws IOException {
        return doGet(url, params, timeOut, timeOut, timeOut, retry, false);
    }

    /**
     * @param url
     * @param params
     * @param timeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int timeOut, int retry, boolean useProxy) throws IOException {
        return doGet(url, params, timeOut, timeOut, timeOut, retry, useProxy);
    }

    /**
     * @param url
     * @param params
     * @param connectTimeOut
     * @param readTimeOut
     * @param writeTimeOut
     * @param retry
     * @return
     * @throws IOException
     */
    public static Response doGet(String url, Map<String, String> params, int connectTimeOut, int readTimeOut, int writeTimeOut, int retry, boolean useProxy) throws IOException {
        OkHttpClient okHttpClient = new OkHttpClient();
        OkHttpClient.Builder okHttpClientBuilder = getOkHttpClientBuilder(okHttpClient, useProxy, url);
        if (connectTimeOut > 0) {
            okHttpClientBuilder.connectTimeout(connectTimeOut, TimeUnit.MILLISECONDS);
        }
        if (readTimeOut > 0) {
            okHttpClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        }
        if (writeTimeOut > 0) {
            okHttpClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        }

        okHttpClient = okHttpClientBuilder.build();

        //封装get请求的url
        if (params != null && !params.isEmpty()) {
            for (Map.Entry entry : params.entrySet()) {
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                if (url.indexOf("?") > 0) {
                    url += "&" + key + "=" + value;
                } else {
                    url += "?" + key + "=" + value;
                }
            }
        }

        Request request = new Request.Builder().url(url).build();

        return retryProcess(okHttpClient, request, retry);
    }


    /**
     * @param request
     * @param retry
     * @return
     * @throws IOException
     */
    private static Response retryProcess(OkHttpClient okHttpClient, Request request, int retry) throws IOException {
        Response response = null;
        IOException ioE = null;
        if (retry > 0) {
            while (retry > 0) {
                try {
                    Call call = okHttpClient.newCall(request);
                    response = call.execute();
                } catch (IOException e) {
                    response = null;
                    logger.error("链接超时或失败 url:" + request.url());
                    ioE = e;
                }
                retry--;
                if (null != response) {
                    break;
                }
            }
            if (response != null) {
                return response;
            } else {
                throw ioE;
            }
        } else {
            Call call = okHttpClient.newCall(request);
            return call.execute();
        }
    }

    /**
     * @param okHttpClient
     * @param useProxy
     * @return
     */
    private static OkHttpClient.Builder getOkHttpClientBuilder(OkHttpClient okHttpClient, boolean useProxy, String url) {
        OkHttpClient.Builder okHttpClientBuilder = okHttpClient.newBuilder();
        if (useProxy) {
            okHttpClientBuilder.proxySelector(new ProxySelector() {
                @Override
                public List<Proxy> select(URI uri) {
                    List<Proxy> proxyList = new ArrayList<Proxy>();
                    proxyList.add(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyAddr, proxyPort)));
                    return proxyList;
                }

                @Override
                public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
                }
            }).proxyAuthenticator(new Authenticator() {
                @Override
                public Request authenticate(Route route, Response response) throws IOException {
                    if (response.request().header("Proxy-Authorization") != null) {
                        // Give up, we've already failed to authenticate.
                        return null;
                    }
                    String credential = Credentials.basic(proxyUser, proxyPwd);
                    return response.request().newBuilder()
                            .header("Proxy-Authorization", credential)
                            .build();
                }
            });
        }
        if (url != null && url.startsWith("https")) {
            okHttpClientBuilder.sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                    .hostnameVerifier(new TrustAllHostnameVerifier());
            return okHttpClientBuilder;
        }
        return okHttpClientBuilder;
    }


    public static void main(String[] args) throws IOException {
        String url = "https://www.baidu.com";
//        String url = "http://www.mydrivers.com/zhuanti/tianti/cpu/";
//        Response response = doPost(url, null,false);
        Response response = doPost(url, null, true);
        System.out.println(response.body().string());

//        String url = "https://apidev.st-creditech.com:1443/api/data/enterpriseCredit";
//        Map<String, String> params = new HashMap<String, String>();
//        params.put("partner_code", "shtxinside");
//        params.put("partner_key", "1541487447147ADC610D2A88733DA647");
//        params.put("enterprise_name", "无锡市信康运输有限公司");
//        params.put("country_subdivision_code", "320000");
//
//        Response response = doPost(url, params,false);
//        System.out.println(response.body().string());
    }
}
